﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MiscUtil.Threading;
using System.Threading;

namespace Rappelz.GameServer
{
    public class RappelzScheduler
    {
        private static long[] UpdateTime = {0x7FFFFFFF, 0x165A0BC00, 0x23C34600, 0x1C9C380, 0x989680, 0x0F4240, 0};

        public static object destroyLock = new object();

        public delegate void SchedulerProcess(object data);

#region Protected Variables
        /// <summary>
        /// A pool of threads for our database processing
        /// </summary>
        protected CustomThreadPool m_ThreadPool;
        protected CustomThreadPool m_DestroyThreadPool;

        /// <summary>
        /// The instance!
        /// </summary>
        protected static RappelzScheduler m_instance;
#endregion


#region Properties
        /// <summary>
        /// Returns the instance
        /// </summary>
        public static RappelzScheduler Instance
        {
            get { return m_instance; }
        }

        /// <summary>
        /// Returns the scheduler thread pool
        /// </summary>
        public CustomThreadPool ThreadPool
        {
            get { return m_ThreadPool; }
        }
#endregion

#region Instance Construction
        /// <summary>
        /// Creates our GameDBManager instance
        /// </summary>
        public static void CreateInstance(uint thread_count)
        {
            if (m_instance == null)
                m_instance = new RappelzScheduler(thread_count);
        }

        /// <summary>
        /// Constructs an AuroraServer object
        /// </summary>
        public RappelzScheduler(uint thread_count)
        {

            thread_count = 1;
            this.m_nThreadCount = (int)thread_count;
//            m_ThreadPool = new CustomThreadPool("Scheduler Threads");
//            m_ThreadPool.SetMinMaxThreads((int)thread_count, (int)thread_count);
//            m_ThreadPool.StartMinThreads();
            

            this.m_pArSchedulerInfo = new RappelzSchedulerInfo[thread_count];
            for (int i = 0; i < thread_count; ++i)
            {
                this.m_pArSchedulerInfo[i] = new RappelzSchedulerInfo();
                // Create the thread object, passing in the Alpha.Beta method
                // via a ThreadStart delegate. This does not start the thread.
                Thread oThread = new Thread(new ParameterizedThreadStart(RappelzScheduler.Engine));

                // Start the thread
                oThread.Start(this.m_pArSchedulerInfo[i]);

            }

            Thread dThread = new Thread(new ParameterizedThreadStart(RappelzScheduler.ObjectDestroyer));

            // Start the thread
            dThread.Start(this.m_pArObjectDestroyerInfo);


        }
#endregion


// Function       :   public void ArScheduler(const class ArScheduler &)
// Function       :   public void ArScheduler::ArScheduler()
// Function       :   public void ArScheduler::~ArScheduler()

        public bool DeInit()
        {
            int i = 0;
            bool result;
            if (this.m_pArSchedulerInfo != null)
            {
                for(i = 0; i < this.m_nThreadCount;++i)
                {
                    if(this.m_pArSchedulerInfo[i] != null)
                    {
                        this.m_pArSchedulerInfo[i].bStop = true;

                    }
                }

                this.m_pArObjectDestroyerInfo.bReqStop = true;

                for(i = 0; i < this.m_nThreadCount;++i)
                {
                    if(this.m_pArSchedulerInfo[i] != null)
                    {
                        while(this.m_pArSchedulerInfo[i].bStop)
                            Thread.Sleep(50);

                    }
                }

//                 v7 = v1->m_pArSchedulerInfo;
//                 if ( v7 )
//                     ArSchedulerInfo::_vector_deleting_destructor_(v7, 3u);
//                 v8 = v1->m_pArObjectDestroyerInfo;
//                 v1->m_pArSchedulerInfo = 0;
//                 CloseHandle(v8->hThread);
//                 v1->m_pArObjectDestroyerInfo->bReqStop = 1;
//                 while ( !v1->m_pArObjectDestroyerInfo->bIsFinished )
//                     Sleep(0x32u);
//                 v9 = v1->m_pArObjectDestroyerInfo;
//                 if ( v9 )
//                 {
//                     ArSchedulerInfo::_ArSchedulerInfo(v1->m_pArObjectDestroyerInfo);
//                     operator delete(v9);
//                 }
//                 v1->m_pArObjectDestroyerInfo = 0;
                result = true;
            }
            else
            {
                result = false;
            }
            return result;
        }

        public void DeleteObject(SchedulerObject obj)
        {
            lock (destroyLock)
            {
                obj.bIsDeleteRequested = true;
                this.SetObjectPriority(obj, SchedulerObject.Priority.Idle);
                if(this.m_pArObjectDestroyerInfo.bIsFinished)
                    this.m_pArObjectDestroyerInfo.vDeleteList.Add(obj);
                else
                    this.m_pArObjectDestroyerInfo.vWaitList.Add(obj);
            }
        }


        /// <summary>
        /// Callback for our worker threads
        /// </summary>
        public static void onProcess(object data)
        {
            try
            {
                IBaseProc db = data as IBaseProc;
                db.onProcess(0);
                Thread.Sleep(1);
            }
            catch (Exception ex)
            {
                Globals.Log.Error("onProcess Exception! - {0}", ex);
            }
        }

        public void SetObjectPriority(SchedulerObject obj, SchedulerObject.Priority priority)
        {
            SchedulerProcess p = RappelzScheduler.onProcess;
//            m_ThreadPool.AddWorkItem(p, obj);

            uint pending_thread = 0;
            if (this.m_pArSchedulerInfo != null)
            {
                if (obj.thread_index == -1 )
                {
                    if (obj.pending_thread_index == -1 )
                        pending_thread = this.getLazyThread();
                    else
                        pending_thread = (uint)obj.pending_thread_index;
                }
                else
                {
                    pending_thread = (uint)obj.thread_index;
                }

                RappelzSchedulerInfo rsi = this.m_pArSchedulerInfo[pending_thread];

                SchedulerObject.Priority op;
                SchedulerObject.Priority np;

                lock(rsi.vModifyList)
                {
                    if (obj.bIsDeleteRequested)
                    {
                        if (obj.pending_priority == SchedulerObject.Priority.Null)
                            op = obj.priority;
                        else
                            op = obj.pending_priority;

                        if (op == SchedulerObject.Priority.Idle)
                        {
                            return;
                        }
                        if (priority != SchedulerObject.Priority.Idle)
                        {
                            return;
                        }
                        if (obj.priority == SchedulerObject.Priority.Idle)
                        {
                            return;
                        }
                    }
                    else
                    {
                        op = priority;
                    }

                    if (obj.pending_priority == SchedulerObject.Priority.Null)
                        np = obj.priority;
                    else
                        np = obj.pending_priority;
                    if (np != op)
                    {
                        if (obj.pending_priority == SchedulerObject.Priority.Null)
                        {
                            if ((obj.priority != SchedulerObject.Priority.Idle || op == SchedulerObject.Priority.Idle)
                                && (obj.priority == SchedulerObject.Priority.Idle || op != SchedulerObject.Priority.Idle))
                            {
                                obj.priority = op;
                            }
                            else
                            {
                                RappelzModifyTag mt = new RappelzModifyTag();
                                mt.obj = obj;
                                mt.bFlag = true;
                                rsi.vModifyList.Add(mt);

                                obj.pending_priority_queue_index = (rsi.vModifyList.Count-1);
                                obj.pending_thread_index = (int)pending_thread;
                                obj.pending_priority = priority;
                            }
                        }
                        else
                        {
                            if (obj.priority != op)
                            {
                                obj.pending_priority = op;
                                obj.pending_thread_index = (int)pending_thread;
                            }
                            else
                            {
                                if(rsi.vObjectList.Count > 0)
                                {
                                    SchedulerObject lbo = rsi.vObjectList.Last();
                                    //                                 lbo.pending_priority_queue_index = obj.pending_priority_queue_index;
                                    //                                 v29 = std::vector<_DUNGEON_RAID_INFO___std::allocator<_DUNGEON_RAID_INFO__>>::back(v10);
                                    //                                 v30 = v5->pending_priority_queue_index;
                                    //                                 *(v3 - 20) = *v29;
                                    //                                 *(v3 - 16) = 1;
                                    //                                 v31 = std::vector<XPerformanceGuard::_TAG_std::allocator<XPerformanceGuard::_TAG>>::operator__(
                                    //                                           &v10->vModifyList,
                                    //                                           v30);
                                    //                                 v31->pObj = *(v3 - 20);
                                    //                                 *&v31->bFlag = *(v3 - 16);
                                    //                                 v32 = *(v3 + 8);
                                    RappelzModifyTag mt = new RappelzModifyTag();
                                    mt.obj = lbo;
                                    mt.bFlag = true;

                                    obj.pending_priority_queue_index = -1;
                                    obj.pending_priority = SchedulerObject.Priority.Null;
                                    obj.pending_thread_index = -1;

                                }
                            }
                        }
                    }

                }
                
            }
        }

        private uint getLazyThread()
        {
            return 0;
//            return (uint)(Interlocked.Add(ref this.m_nInstructionCount, 1) % this.m_nThreadCount);
        }

        public static void ObjectDestroyer(object obj)
        {
// .text:0040A443 result          = std::_Vector_iterator<ArSchedulerObject *,std::allocator<ArSchedulerObject *> > ptr -3Ch
// .text:0040A443 _First          = std::_Vector_iterator<ArSchedulerObject *,std::allocator<ArSchedulerObject *> > ptr -34h
// .text:0040A443 _Last           = std::_Vector_iterator<ArSchedulerObject *,std::allocator<ArSchedulerObject *> > ptr -2Ch
// .text:0040A443 this            = std::_Vector_const_iterator<unsigned int,std::allocator<unsigned int> > ptr -24h
// .text:0040A443 _Right          = std::_Vector_const_iterator<ArObject *,std::allocator<ArObject *> > ptr -1Ch
// .text:0040A443 it              = std::_Vector_iterator<ArSchedulerObject *,std::allocator<ArSchedulerObject *> > ptr -14h
            uint dwPrevTickCount;           // = dword ptr -0Ch
            int nDestroyedCount;            // = dword ptr -8
            int nDestroyerLoopCount;        // = dword ptr -4
// .text:0040A443 pArg            = dword ptr  8

// Data           :   ebp Relative, [FFFFFFEC], Local, Type: class std::_Vector_iterator<ArSchedulerObject *,std::allocator<ArSchedulerObject *> >, it
            RappelzObjectDestroyerInfo scheduler = obj as RappelzObjectDestroyerInfo;
            if(obj == null)
                return;

            nDestroyedCount = 0;
            nDestroyerLoopCount = 0;

            dwPrevTickCount = (uint)Globals.Time64();//GetTickCount();

            while (true)
            {
                nDestroyerLoopCount++;

                lock (destroyLock)
                {
                    try
                    {
                        if (scheduler.bReqStop)
                        {
                            if (scheduler.vDeleteList.Count == 0)
                                break;
                        }
                        foreach (SchedulerObject so in scheduler.vDeleteList)
                        {
                            //                        spinlock_enter(obj.lock);
                            bool bNeedsWait = false;
                            if (!scheduler.bReqStop)
                            {
                                if (!RappelzScheduler.IsDeleteable(so))
                                {
                                    // spin on lock I guess
                                    //                                 _EBX = LODWORD(std::_Vector_const_iterator<PartyManager::PartyInfo___std::allocator<PartyManager::PartyInfo__>>::operator_(&it)->end.x)
                                    //                                      + 52;
                                    //                                 _EAX = 0;
                                    //                                 __asm { lock xchg eax, [ebx] }
                                    scheduler.vWaitList.Add(so);
                                    bNeedsWait = true;
                                }
                            }
                            if (!bNeedsWait)
                            {
                                so.m_bIsEnable = false;
                                //                         _EBX = LODWORD(std::_Vector_const_iterator<PartyManager::PartyInfo___std::allocator<PartyManager::PartyInfo__>>::operator_(&it)->end.x)
                                //                              + 52;
                                //                         _EAX = 0;
                                //                         __asm { lock xchg eax, [ebx] }
                                if (!so.ProcDelete())
                                {
                                    scheduler.vWaitList.Add(so);
                                    bNeedsWait = true;
                                }
                                else
                                {
                                    nDestroyedCount++;
                                }
                            }
                        }

                        scheduler.vDeleteList.Clear();
                        if (scheduler.vWaitList.Count > 0)
                        {
                            List<SchedulerObject> tl = scheduler.vWaitList;
                            scheduler.vWaitList = scheduler.vDeleteList;
                            scheduler.vDeleteList = tl;

                        }
                    }
                    catch (System.Exception)
                    {
                    	
                    }
                }


                uint ctc = (uint)Globals.Time64();
                if (ctc - dwPrevTickCount < 100)
                    Thread.Sleep((int)(ctc - dwPrevTickCount + 100));
                dwPrevTickCount = ctc;
            }
//            (*(*v4 + 12))(v4);
            scheduler.bIsFinished = true;
        }

        public static bool IsDeleteable(SchedulerObject pPtr)
        {
            bool result = pPtr.IsDeleteable();
//             if (result)
//                 result = ArSchedulerObject::IsDeleteable(pPtr) != 0;
            return result;
        }


        public static void Engine(object obj)
        {
            RappelzSchedulerInfo scheduler = obj as RappelzSchedulerInfo;
            uint nLoopDelayCount;

//             if ( s_pfInitFunc )
//             {
//                 *&buf[1004] = v1->nThreadIndex;
//                 s_pfInitFunc(*&buf[1004]);
//             }
            while (!scheduler.bStop)
            {
                scheduler.nInstructionCnt++;
                nLoopDelayCount = (uint)Globals.Time64();

                lock(scheduler.vObjectList)
                {
                    foreach (SchedulerObject bo in scheduler.vObjectList)
                    {
                        if (scheduler.bStop)
                            break;
                        if (bo.m_bIsEnable)
                        {
                            if (bo.last_proc_tick != scheduler.nInstructionCnt)
                            {
                                if (!bo.bIsDeleteRequested)
                                {
                                    long ctl = DateTime.Now.Ticks;
                                    if ((bo.last_proc_time + (UpdateTime[(int)bo.priority]) < (ctl)))
                                    {
                                        bo.last_proc_time = ctl;
                                        bo.last_proc_tick = scheduler.nInstructionCnt;
                                        bo.onProcess((int)scheduler.nThreadIndex);
                                    }
                                }
                            }
                        }
                    }
                }

                lock(scheduler.vModifyList)
                {
                    foreach (RappelzModifyTag mt in scheduler.vModifyList)
                    {
                        if (mt.obj.pending_thread_index == -1 )
                        {
                            break;
                        }
                        if ((int)mt.obj.priority != 0 && mt.obj.thread_index != scheduler.nThreadIndex)
                        {
                            break;
                        }
                        if (mt.obj.pending_thread_index != scheduler.nThreadIndex)
                        {
                            break;
                        }

                        if ((int)mt.obj.pending_priority == -1)
                        {
                            break;
                        }
                        if ((int)mt.obj.pending_priority != 0)
                        {
                            mt.obj.thread_index = (int)scheduler.nThreadIndex;
                            if ((int)mt.obj.priority == 0)
                            {
                                mt.obj.priority = mt.obj.pending_priority;
                                if (!scheduler.vObjectList.Contains(mt.obj))
                                {
                                    scheduler.vObjectList.Add(mt.obj);
                                    mt.obj.priority_queue_index = scheduler.vObjectList.Count - 1;
                                    scheduler.nObjectCount++;
                                }
                            }
                        }
                        else
                        {
                            if (mt.obj.priority != 0)
                            {
                                // remove it from the modify list
                                scheduler.nObjectCount--;
                            }
                        }
                    }
    
                    scheduler.vModifyList.Clear();
                }
                uint nTmp32 = (uint)Globals.Time64();
                if (nLoopDelayCount + 100 > nTmp32)
                {
                    Thread.Sleep((int)(nLoopDelayCount - nTmp32 + 100));
                }
            }
            scheduler.bStop = false;
        }

        public int m_nThreadCount;             // 0x4
        public int m_nInstructionCount;        // 0x8
        public RappelzSchedulerInfo[] m_pArSchedulerInfo;   // 0xC
        public RappelzObjectDestroyerInfo m_pArObjectDestroyerInfo = new RappelzObjectDestroyerInfo();  // Data           :   this+0x10, Member, Type: struct ArObjectDestroyerInfo *, 
    }

    public class RappelzSchedulerInfo
    {
//         Function       :   public void ArSchedulerInfo(const struct ArSchedulerInfo &)
//         Function       :   public void ArSchedulerInfo::ArSchedulerInfo()
        public List<SchedulerObject> vObjectList = new List<SchedulerObject>();                       // 0x0
        public List<RappelzModifyTag> vModifyList = new List<RappelzModifyTag>();           // 0x10
//        Data           :   this+0x20, Member, Type: class XCriticalSection, modifyLock

        public uint nInstructionCnt;                            // 0x60
        public uint nObjectCount;                               // 0x64
        public bool bStop;                                      // 0x68
        public object hThread;                                  // 0x6C
        public uint nThreadIndex;                               // 0x70
    }

    public class RappelzObjectDestroyerInfo
    {
// Function       :   public void ArObjectDestroyerInfo(const struct ArObjectDestroyerInfo &)
// Function       :   public void ArObjectDestroyerInfo::ArObjectDestroyerInfo()
// Function       :   public void ArObjectDestroyerInfo::~ArObjectDestroyerInfo()
        public List<SchedulerObject> vDeleteList = new List<SchedulerObject>();                       // 0x0
        public List<SchedulerObject> vWaitList = new List<SchedulerObject>();                         // 0x10
// Data           :   this+0x20, Member, Type: class XCriticalSection, destroyLock
// UserDefinedType:     XCriticalSection
// 
        public object hThread;                                  // 0x60, Member, Type: void *, hThread
        public bool bReqStop;                                   // 0x64, Member, Type: volatile bool, 
        public bool bIsFinished;                                // 0x65, Member, Type: volatile bool, 
    }

    public class RappelzModifyTag
    {
        public SchedulerObject obj;
        public bool bFlag;
    }
}
